use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::process::Command;
use std::ptr;
use std::slice;
use std::str;
use std::thread;
use std::time::Duration;

const SHM_SIZE: usize = 1024;
const SHM_NAME: &str = "shm_example";

#[repr(C)]
struct Message {
    written: i32,
    text: [u8; SHM_SIZE - std::mem::size_of::<i32>()],
}

fn main() {
    let shm_fd = unsafe {
        libc::shm_open(
            SHM_NAME.as_ptr() as *const libc::c_char,
            libc::O_CREAT | libc::O_RDWR,
            0o666,
        )
    };
    if shm_fd < 0 {
        eprintln!("Failed to create shared memory");
        return;
    }

    if unsafe { libc::ftruncate(shm_fd, SHM_SIZE as libc::off_t) } < 0 {
        eprintln!("Failed to set shared memory size");
        unsafe { libc::close(shm_fd) };
        return;
    }

    let shm_ptr = unsafe {
        libc::mmap(
            ptr::null_mut(),
            SHM_SIZE,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_SHARED,
            shm_fd,
            0,
        )
    };
    if shm_ptr == libc::MAP_FAILED {
        eprintln!("Failed to map shared memory");
        unsafe { libc::close(shm_fd) };
        return;
    }

    let shm = unsafe { &mut *(shm_ptr as *mut Message) };
    shm.written = 0;

    let pid = unsafe { libc::fork() };
    if pid < 0 {
        eprintln!("Failed to fork");
        unsafe {
            libc::munmap(shm_ptr, SHM_SIZE);
            libc::close(shm_fd);
        };
        return;
    }

    if pid == 0 {
        // Child process - client
        client(shm);
    } else {
        // Parent process - server
        server(shm);
    }

    unsafe {
        libc::munmap(shm_ptr, SHM_SIZE);
        libc::close(shm_fd);
        libc::shm_unlink(SHM_NAME.as_ptr() as *const libc::c_char);
    }
}

fn client(shm: &mut Message) {
    loop {
        while shm.written == 1 {
            thread::sleep(Duration::from_secs(1));
        }

        print!("Enter some text: ");
        std::io::stdout().flush().unwrap();
        let mut buf = [0; 1024];
        std::io::stdin().read(&mut buf).unwrap();

        let text = str::from_utf8(&buf).unwrap();
        shm.text[..text.len()].copy_from_slice(text.as_bytes());
        shm.written = 1;
    }
}

fn server(shm: &mut Message) {
    loop {
        while shm.written == 0 {
            thread::sleep(Duration::from_secs(1));
        }

        println!("Server received: {}", str::from_utf8(&shm.text).unwrap());
        shm.written = 0;
    }
}